哈囉,大家好!在前面的文章中,我們已經規劃了前端介面,並盤點了所需的頁面與功能。
現在,是時候開始動手實作了。今天,我們將專注於 銀行帳戶列表頁面(Bank Accounts)以及 新增/編輯銀行帳戶頁面(Add/Edit Bank Account)的開發。
透過這次的實作,我們將學習如何在 Nuxt 中建立頁面、與後端 API 互動,以及實作表單驗證等功能。
讓我們一起讓應用程式更加完整吧!
首先,我們要建立銀行帳戶列表頁面,讓使用者可以查看和管理他們的銀行帳戶。
在 pages/ 目錄下建立 bank-accounts.vue 檔案。
touch pages/bank-accounts.vue
在 bank-accounts.vue 中,我們先建立基本的頁面結構。
<template>
<div>
<h2 class="text-2xl font-bold mb-4">銀行帳戶管理</h2>
<div v-if="error" class="text-red-500">
{{ error }}
</div>
<div v-else>
<button @click="goToAddAccount" class="bg-blue-600 text-white px-4 py-2 mb-4">
新增銀行帳戶
</button>
<table class="w-full text-left">
<thead>
<tr>
<th class="border px-4 py-2">帳戶名稱</th>
<th class="border px-4 py-2">銀行名稱</th>
<th class="border px-4 py-2">帳戶餘額</th>
<th class="border px-4 py-2">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="account in bankAccounts" :key="account.id">
<td class="border px-4 py-2">{{ account.account_name }}</td>
<td class="border px-4 py-2">{{ account.bank_name }}</td>
<td class="border px-4 py-2">{{ account.balance }}</td>
<td class="border px-4 py-2">
<button @click="goToEditAccount(account.id)" class="text-blue-600 mr-2">
編輯
</button>
<button @click="deleteAccount(account.id)" class="text-red-600">
刪除
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
middleware: 'auth',
data() {
return {
bankAccounts: [],
error: null,
}
},
async mounted() {
try {
const response = await this.$axios.get('/api/bank-accounts')
if (response.data.status === 'success') {
this.bankAccounts = response.data.data
} else {
this.error = response.data.message || '無法獲取銀行帳戶資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching bank accounts:', error)
}
},
methods: {
goToAddAccount() {
this.$router.push('/bank-accounts/add')
},
goToEditAccount(id) {
this.$router.push(`/bank-accounts/edit/${id}`)
},
async deleteAccount(id) {
if (confirm('確定要刪除這個銀行帳戶嗎?')) {
try {
await this.$axios.delete(`/api/bank-accounts/${id}`)
this.bankAccounts = this.bankAccounts.filter(account => account.id !== id)
} catch (error) {
alert('刪除失敗,請稍後再試。')
console.error('Error deleting bank account:', error)
}
}
},
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
接下來,我們需要建立一個頁面,讓使用者可以新增或編輯銀行帳戶。我們可以使用同一個頁面,根據路由參數來判斷是新增還是編輯。
在 pages/bank-accounts/ 目錄下建立 _form.vue 檔案。
mkdir -p pages/bank-accounts
touch pages/bank-accounts/add.vue
touch pages/bank-accounts/edit.vue
touch pages/bank-accounts/_form.vue
我們先在 _form.vue 中建立表單元件,供新增和編輯頁面使用。
<template>
<div>
<h2 class="text-2xl font-bold mb-4">{{ isEdit ? '編輯銀行帳戶' : '新增銀行帳戶' }}</h2>
<form @submit.prevent="submitForm">
<div class="mb-4">
<label class="block">帳戶名稱</label>
<input v-model="form.account_name" type="text" class="border p-2 w-full" required />
<p v-if="errors.account_name" class="text-red-500">{{ errors.account_name }}</p>
</div>
<div class="mb-4">
<label class="block">銀行名稱</label>
<input v-model="form.bank_name" type="text" class="border p-2 w-full" />
<p v-if="errors.bank_name" class="text-red-500">{{ errors.bank_name }}</p>
</div>
<div class="mb-4">
<label class="block">帳號</label>
<input v-model="form.account_number" type="text" class="border p-2 w-full" />
<p v-if="errors.account_number" class="text-red-500">{{ errors.account_number }}</p>
</div>
<div class="mb-4">
<label class="block">帳戶餘額</label>
<input v-model="form.balance" type="number" step="0.01" class="border p-2 w-full" required />
<p v-if="errors.balance" class="text-red-500">{{ errors.balance }}</p>
</div>
<div v-if="error" class="text-red-500 mb-4">
{{ error }}
</div>
<button type="submit" class="bg-blue-600 text-white px-4 py-2">
{{ isEdit ? '更新' : '新增' }}
</button>
</form>
</div>
</template>
<script>
export default {
props: {
isEdit: {
type: Boolean,
default: false,
},
accountId: {
type: Number,
default: null,
},
},
data() {
return {
form: {
account_name: '',
bank_name: '',
account_number: '',
balance: 0,
},
errors: {},
error: null,
}
},
async mounted() {
if (this.isEdit && this.accountId) {
try {
const response = await this.$axios.get(`/api/bank-accounts/${this.accountId}`)
if (response.data.status === 'success') {
this.form = response.data.data
} else {
this.error = response.data.message || '無法獲取帳戶資料。'
}
} catch (error) {
this.error = '伺服器發生錯誤,請稍後再試。'
console.error('Error fetching bank account:', error)
}
}
},
methods: {
async submitForm() {
this.errors = {}
this.error = null
try {
if (this.isEdit) {
await this.$axios.put(`/api/bank-accounts/${this.accountId}`, this.form)
} else {
await this.$axios.post('/api/bank-accounts', this.form)
}
this.$router.push('/bank-accounts')
} catch (error) {
if (error.response && error.response.status === 422) {
this.errors = error.response.data.errors
} else {
this.error = '提交失敗,請稍後再試。'
}
console.error('Error submitting bank account form:', error)
}
},
},
}
</script>
<style scoped>
/* 元件專屬的樣式 */
</style>
<template>
<FormComponent />
</template>
<script>
import FormComponent from './_form.vue'
export default {
middleware: 'auth',
components: {
FormComponent,
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
<template>
<FormComponent :isEdit="true" :accountId="accountId" />
</template>
<script>
import FormComponent from './_form.vue'
export default {
middleware: 'auth',
components: {
FormComponent,
},
computed: {
accountId() {
return parseInt(this.$route.params.id)
},
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
由於我們使用了動態路由參數,需要在 pages/bank-accounts/ 目錄下調整檔案結構。
mv pages/bank-accounts/edit.vue pages/bank-accounts/_id.vue
<template>
<FormComponent :isEdit="true" :accountId="accountId" />
</template>
<script>
import FormComponent from './_form.vue'
export default {
middleware: 'auth',
components: {
FormComponent,
},
computed: {
accountId() {
return parseInt(this.$route.params.id)
},
},
}
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
為了方便使用者訪問銀行帳戶管理頁面,我們需要在導航列中添加對應的連結。
<template>
<header class="bg-blue-600 text-white p-4">
<nav class="container mx-auto flex justify-between">
<h1 class="text-xl font-bold">
<NuxtLink to="/">財務管理系統</NuxtLink>
</h1>
<ul class="flex space-x-4 items-center">
<li><NuxtLink to="/transactions">交易紀錄</NuxtLink></li>
<li><NuxtLink to="/bank-accounts">銀行帳戶</NuxtLink></li>
<li><NuxtLink to="/categories">分類管理</NuxtLink></li>
<li><NuxtLink to="/reports">報表</NuxtLink></li>
<li v-if="$store.getters['auth/isAuthenticated']">
<div class="relative" @click="toggleMenu">
<span>{{ $store.state.auth.user.username }}</span>
<div v-if="showMenu" class="absolute right-0 mt-2 bg-white text-black p-2">
<NuxtLink to="/profile">個人資料</NuxtLink>
<a href="#" @click.prevent="logout">登出</a>
</div>
</div>
</li>
<li v-else>
<NuxtLink to="/login">登入</NuxtLink>
</li>
</ul>
</nav>
</header>
</template>
<script>
export default {
data() {
return {
showMenu: false,
}
},
methods: {
toggleMenu() {
this.showMenu = !this.showMenu
},
logout() {
this.$store.commit('auth/clearToken')
this.$router.push('/login')
},
},
}
</script>
<style scoped>
/* Header 專屬的樣式 */
</style>
現在,我們已經完成了銀行帳戶列表頁面和新增/編輯頁面的開發。讓我們進行測試,確保功能正常運作。
今天,我們成功地實作了銀行帳戶列表頁面以及新增/編輯頁面。透過這次的開發,我們學習了: